/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.dsl;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.aggregator.AggregatingMessageHandler;
import org.springframework.integration.aggregator.BarrierMessageHandler;
import org.springframework.integration.channel.ChannelInterceptorAware;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.integration.channel.FluxMessageChannel;
import org.springframework.integration.channel.MessageChannelReactiveUtils;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.dsl.channel.MessageChannelSpec;
import org.springframework.integration.dsl.channel.WireTapSpec;
import org.springframework.integration.dsl.support.FixedSubscriberChannelPrototype;
import org.springframework.integration.dsl.support.MessageChannelReference;
import org.springframework.integration.expression.ControlBusMethodFilter;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
import org.springframework.integration.filter.MessageFilter;
import org.springframework.integration.filter.MethodInvokingSelector;
import org.springframework.integration.handler.AbstractMessageProducingHandler;
import org.springframework.integration.handler.BeanNameMessageProcessor;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.integration.handler.DelayHandler;
import org.springframework.integration.handler.ExpressionCommandMessageProcessor;
import org.springframework.integration.handler.GenericHandler;
import org.springframework.integration.handler.LambdaMessageProcessor;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.handler.MessageTriggerAction;
import org.springframework.integration.handler.MethodInvokingMessageProcessor;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.router.AbstractMessageRouter;
import org.springframework.integration.router.ErrorMessageExceptionTypeRouter;
import org.springframework.integration.router.ExpressionEvaluatingRouter;
import org.springframework.integration.router.MethodInvokingRouter;
import org.springframework.integration.router.RecipientListRouter;
import org.springframework.integration.scattergather.ScatterGatherHandler;
import org.springframework.integration.splitter.AbstractMessageSplitter;
import org.springframework.integration.splitter.DefaultMessageSplitter;
import org.springframework.integration.splitter.ExpressionEvaluatingSplitter;
import org.springframework.integration.splitter.MethodInvokingSplitter;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.support.MapBuilder;
import org.springframework.integration.transformer.ClaimCheckInTransformer;
import org.springframework.integration.transformer.ClaimCheckOutTransformer;
import org.springframework.integration.transformer.ContentEnricher;
import org.springframework.integration.transformer.ExpressionEvaluatingTransformer;
import org.springframework.integration.transformer.GenericTransformer;
import org.springframework.integration.transformer.HeaderFilter;
import org.springframework.integration.transformer.MessageTransformingHandler;
import org.springframework.integration.transformer.MethodInvokingTransformer;
import org.springframework.integration.transformer.Transformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.util.function.Tuple2;
/**
* The {@code Builder} pattern implementation for the EIP-method chain.
* Provides a variety of methods to populate Spring Integration components
* to an {@link IntegrationFlow} for the future registration in the
* application context.
*
* @param <B> the {@link IntegrationFlowDefinition} implementation type.
*
* @author Artem Bilan
* @author Gary Russell
* @author Gabriele Del Prete
*
* @since 5.0
*
* @see org.springframework.integration.config.dsl.IntegrationFlowBeanPostProcessor
*/
public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinition<B>> {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private static final Set<MessageProducer> REFERENCED_REPLY_PRODUCERS = new HashSet<>();
protected final Set<Object> integrationComponents = new LinkedHashSet<>();
protected MessageChannel currentMessageChannel;
protected Object currentComponent;
private StandardIntegrationFlow integrationFlow;
private boolean implicitChannel;
IntegrationFlowDefinition() {
}
B addComponent(Object component) {
this.integrationComponents.add(component);
return _this();
}
B addComponents(Collection<Object> components) {
if (components != null) {
this.integrationComponents.addAll(components);
}
return _this();
}
B currentComponent(Object component) {
this.currentComponent = component;
return _this();
}
/**
* Populate an {@link org.springframework.integration.channel.FixedSubscriberChannel} instance
* at the current {@link IntegrationFlow} chain position.
* The 'bean name' will be generated during the bean registration phase.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B fixedSubscriberChannel() {
return fixedSubscriberChannel(null);
}
/**
* Populate an {@link org.springframework.integration.channel.FixedSubscriberChannel} instance
* at the current {@link IntegrationFlow} chain position.
* The provided {@code messageChannelName} is used for the bean registration.
* @param messageChannelName the bean name to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B fixedSubscriberChannel(String messageChannelName) {
return channel(new FixedSubscriberChannelPrototype(messageChannelName));
}
/**
* Populate a {@link MessageChannelReference} instance
* at the current {@link IntegrationFlow} chain position.
* The provided {@code messageChannelName} is used for the bean registration
* ({@link org.springframework.integration.channel.DirectChannel}), if there is no such a bean
* in the application context. Otherwise the existing {@link MessageChannel} bean is used
* to wire integration endpoints.
* @param messageChannelName the bean name to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B channel(String messageChannelName) {
return channel(new MessageChannelReference(messageChannelName));
}
/**
* Populate a {@link MessageChannel} instance
* at the current {@link IntegrationFlow} chain position using the {@link MessageChannelSpec}
* fluent API.
* @param messageChannelSpec the {@link MessageChannelSpec} to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see org.springframework.integration.dsl.channel.MessageChannels
*/
public B channel(MessageChannelSpec<?, ?> messageChannelSpec) {
Assert.notNull(messageChannelSpec, "'messageChannelSpec' must not be null");
return channel(messageChannelSpec.get());
}
/**
* Populate the provided {@link MessageChannel} instance
* at the current {@link IntegrationFlow} chain position.
* The {@code messageChannel} can be an existing bean, or fresh instance, in which case
* the {@link org.springframework.integration.config.dsl.IntegrationFlowBeanPostProcessor}
* will populate it as a bean with a generated name.
* @param messageChannel the {@link MessageChannel} to populate.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B channel(MessageChannel messageChannel) {
Assert.notNull(messageChannel, "'messageChannel' must not be null");
if (this.currentMessageChannel != null) {
bridge();
}
this.currentMessageChannel = messageChannel;
return registerOutputChannelIfCan(this.currentMessageChannel);
}
/**
* Populate a {@link MessageChannel} instance
* at the current {@link IntegrationFlow} chain position using the {@link Channels}
* factory fluent API.
* @param channels the {@link Function} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B channel(Function<Channels, MessageChannelSpec<?, ?>> channels) {
Assert.notNull(channels, "'channels' must not be null");
return channel(channels.apply(new Channels()));
}
/**
* The {@link org.springframework.integration.channel.PublishSubscribeChannel} {@link #channel}
* method specific implementation to allow the use of the 'subflow' subscriber capability.
* @param publishSubscribeChannelConfigurer the {@link Consumer} to specify
* {@link PublishSubscribeSpec} options including 'subflow' definition.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B publishSubscribeChannel(Consumer<PublishSubscribeSpec> publishSubscribeChannelConfigurer) {
return publishSubscribeChannel(null, publishSubscribeChannelConfigurer);
}
/**
* The {@link org.springframework.integration.channel.PublishSubscribeChannel} {@link #channel}
* method specific implementation to allow the use of the 'subflow' subscriber capability.
* Use the provided {@link Executor} for the target subscribers.
* @param executor the {@link Executor} to use.
* @param publishSubscribeChannelConfigurer the {@link Consumer} to specify
* {@link PublishSubscribeSpec} options including 'subflow' definition.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B publishSubscribeChannel(Executor executor,
Consumer<PublishSubscribeSpec> publishSubscribeChannelConfigurer) {
Assert.notNull(publishSubscribeChannelConfigurer, "'publishSubscribeChannelConfigurer' must not be null");
PublishSubscribeSpec spec = new PublishSubscribeSpec(executor);
publishSubscribeChannelConfigurer.accept(spec);
return addComponents(spec.getComponentsToRegister()).channel(spec);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .filter("World"::equals)
* .wireTap(sf -> sf.<String, String>transform(String::toUpperCase))
* .handle(p -> process(p))
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param flow the {@link IntegrationFlow} for wire-tap subflow as an alternative to the {@code wireTapChannel}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(IntegrationFlow flow) {
return wireTap(flow, null);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* f -> f.wireTap("tapChannel")
* .handle(p -> process(p))
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param wireTapChannel the {@link MessageChannel} bean name to wire-tap.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(String wireTapChannel) {
return wireTap(wireTapChannel, null);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .transform("payload")
* .wireTap(tapChannel())
* .channel("foo")
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param wireTapChannel the {@link MessageChannel} to wire-tap.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(MessageChannel wireTapChannel) {
return wireTap(wireTapChannel, null);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .transform("payload")
* .wireTap(sf -> sf.<String, String>transform(String::toUpperCase), wt -> wt.selector("payload == 'foo'"))
* .channel("foo")
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param flow the {@link IntegrationFlow} for wire-tap subflow as an alternative to the {@code wireTapChannel}.
* @param wireTapConfigurer the {@link Consumer} to accept options for the {@link WireTap}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(IntegrationFlow flow, Consumer<WireTapSpec> wireTapConfigurer) {
DirectChannel wireTapChannel = new DirectChannel();
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(wireTapChannel);
flow.configure(flowBuilder);
addComponent(flowBuilder.get());
return wireTap(wireTapChannel, wireTapConfigurer);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .transform("payload")
* .wireTap("tapChannel", wt -> wt.selector(m -> m.getPayload().equals("foo")))
* .channel("foo")
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param wireTapChannel the {@link MessageChannel} bean name to wire-tap.
* @param wireTapConfigurer the {@link Consumer} to accept options for the {@link WireTap}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(String wireTapChannel, Consumer<WireTapSpec> wireTapConfigurer) {
DirectChannel internalWireTapChannel = new DirectChannel();
addComponent(IntegrationFlows.from(internalWireTapChannel).channel(wireTapChannel).get());
return wireTap(internalWireTapChannel, wireTapConfigurer);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .transform("payload")
* .wireTap(tapChannel(), wt -> wt.selector(m -> m.getPayload().equals("foo")))
* .channel("foo")
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param wireTapChannel the {@link MessageChannel} to wire-tap.
* @param wireTapConfigurer the {@link Consumer} to accept options for the {@link WireTap}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(MessageChannel wireTapChannel, Consumer<WireTapSpec> wireTapConfigurer) {
WireTapSpec wireTapSpec = new WireTapSpec(wireTapChannel);
if (wireTapConfigurer != null) {
wireTapConfigurer.accept(wireTapSpec);
}
addComponent(wireTapChannel);
return wireTap(wireTapSpec);
}
/**
* Populate the {@code Wire Tap} EI Pattern specific
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation
* to the current {@link #currentMessageChannel}.
* <p> It is useful when an implicit {@link MessageChannel} is used between endpoints:
* <pre class="code">
* {@code
* .transform("payload")
* .wireTap(new WireTap(tapChannel().selector(m -> m.getPayload().equals("foo")))
* .channel("foo")
* }
* </pre>
* This method can be used after any {@link #channel} for explicit {@link MessageChannel},
* but with the caution do not impact existing {@link org.springframework.messaging.support.ChannelInterceptor}s.
* @param wireTapSpec the {@link WireTapSpec} to use.
* <p> When this EIP-method is used in the end of flow, it appends {@code nullChannel} to terminate flow properly,
* Otherwise {@code Dispatcher has no subscribers} exception is thrown for implicit {@link DirectChannel}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B wireTap(WireTapSpec wireTapSpec) {
WireTap interceptor = wireTapSpec.get();
if (this.currentMessageChannel == null || !(this.currentMessageChannel instanceof ChannelInterceptorAware)) {
this.implicitChannel = true;
channel(new DirectChannel());
}
addComponent(wireTapSpec);
((ChannelInterceptorAware) this.currentMessageChannel).addInterceptor(interceptor);
return _this();
}
/**
* Populate the {@code Control Bus} EI Pattern specific {@link MessageHandler} implementation
* at the current {@link IntegrationFlow} chain position.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionCommandMessageProcessor
*/
public B controlBus() {
return controlBus(null);
}
/**
* Populate the {@code Control Bus} EI Pattern specific {@link MessageHandler} implementation
* at the current {@link IntegrationFlow} chain position.
* @param endpointConfigurer the {@link Consumer} to accept integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionCommandMessageProcessor
* @see GenericEndpointSpec
*/
public B controlBus(Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
return this.handle(new ServiceActivatingHandler(new ExpressionCommandMessageProcessor(
new ControlBusMethodFilter())), endpointConfigurer);
}
/**
* Populate the {@code Transformer} EI Pattern specific {@link MessageHandler} implementation
* for the SpEL {@link Expression}.
* @param expression the {@code Transformer} {@link Expression}.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionEvaluatingTransformer
*/
public B transform(String expression) {
return transform(expression, (Consumer<GenericEndpointSpec<MessageTransformingHandler>>) null);
}
/**
* Populate the {@code Transformer} EI Pattern specific {@link MessageHandler} implementation
* for the SpEL {@link Expression}.
* @param expression the {@code Transformer} {@link Expression}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionEvaluatingTransformer
*/
public B transform(String expression, Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
Assert.hasText(expression, "'expression' must not be empty");
return transform(new ExpressionEvaluatingTransformer(PARSER.parseExpression(expression)),
endpointConfigurer);
}
/**
* Populate the {@code MessageTransformingHandler} for the {@link MethodInvokingTransformer}
* to invoke the discovered service method at runtime.
* @param service the service to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionEvaluatingTransformer
*/
public B transform(Object service) {
return transform(service, null);
}
/**
* Populate the {@code MessageTransformingHandler} for the {@link MethodInvokingTransformer}
* to invoke the service method at runtime.
* @param service the service to use.
* @param methodName the method to invoke.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
*/
public B transform(Object service, String methodName) {
return transform(service, methodName, null);
}
/**
* Populate the {@code MessageTransformingHandler} for the {@link MethodInvokingTransformer}
* to invoke the service method at runtime.
* @param service the service to use.
* @param methodName the method to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see ExpressionEvaluatingTransformer
*/
public B transform(Object service, String methodName,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
MethodInvokingTransformer transformer;
if (StringUtils.hasText(methodName)) {
transformer = new MethodInvokingTransformer(service, methodName);
}
else {
transformer = new MethodInvokingTransformer(service);
}
return transform(transformer, endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}.
* @param genericTransformer the {@link GenericTransformer} to populate.
* @param <S> the source type - 'transform from'.
* @param <T> the target type - 'transform to'.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
*/
public <S, T> B transform(GenericTransformer<S, T> genericTransformer) {
return this.transform(null, genericTransformer);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the
* {@link org.springframework.integration.handler.MessageProcessor} from provided {@link MessageProcessorSpec}.
* <pre class="code">
* {@code
* .transform(Scripts.script("classpath:myScript.py").valiable("foo", bar()))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
*/
public B transform(MessageProcessorSpec<?> messageProcessorSpec) {
return transform(messageProcessorSpec, (Consumer<GenericEndpointSpec<MessageTransformingHandler>>) null);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the
* {@link org.springframework.integration.handler.MessageProcessor} from provided {@link MessageProcessorSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* <pre class="code">
* {@code
* .transform(Scripts.script("classpath:myScript.py").valiable("foo", bar()),
* e -> e.autoStartup(false))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
*/
public B transform(MessageProcessorSpec<?> messageProcessorSpec,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
Assert.notNull(messageProcessorSpec, "'messageProcessorSpec' must not be null");
MessageProcessor<?> processor = messageProcessorSpec.get();
return addComponent(processor)
.transform(new MethodInvokingTransformer(processor), endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}
* for the specific {@code payloadType} to convert at runtime.
* @param payloadType the {@link Class} for expected payload type.
* @param genericTransformer the {@link GenericTransformer} to populate.
* @param <P> the payload type - 'transform from'.
* @param <T> the target type - 'transform to'.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer) {
return this.transform(payloadType, genericTransformer, null);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param genericTransformer the {@link GenericTransformer} to populate.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <S> the source type - 'transform from'.
* @param <T> the target type - 'transform to'.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
* @see GenericEndpointSpec
*/
public <S, T> B transform(GenericTransformer<S, T> genericTransformer,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
return this.transform(null, genericTransformer, endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}
* for the specific {@code payloadType} to convert at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param payloadType the {@link Class} for expected payload type.
* @param genericTransformer the {@link GenericTransformer} to populate.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type - 'transform from'.
* @param <T> the target type - 'transform to'.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
* @see GenericEndpointSpec
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
Assert.notNull(genericTransformer, "'genericTransformer' must not be null");
Transformer transformer = genericTransformer instanceof Transformer ? (Transformer) genericTransformer :
(isLambda(genericTransformer)
? new MethodInvokingTransformer(new LambdaMessageProcessor(genericTransformer, payloadType))
: new MethodInvokingTransformer(genericTransformer));
return addComponent(transformer)
.handle(new MessageTransformingHandler(transformer), endpointConfigurer);
}
/**
* Populate a {@link MessageFilter} with {@link MessageSelector} for the provided SpEL expression.
* @param expression the SpEL expression.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B filter(String expression) {
return filter(expression, (Consumer<FilterEndpointSpec>) null);
}
/**
* Populate a {@link MessageFilter} with {@link MessageSelector} for the provided SpEL expression.
* In addition accept options for the integration endpoint using {@link FilterEndpointSpec}:
* <pre class="code">
* {@code
* .filter("payload.hot"), e -> e.autoStartup(false))
* }
* </pre>
* @param expression the SpEL expression.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see FilterEndpointSpec
*/
public B filter(String expression, Consumer<FilterEndpointSpec> endpointConfigurer) {
Assert.hasText(expression, "'expression' must not be empty");
return filter(new ExpressionEvaluatingSelector(expression), endpointConfigurer);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector} for the
* discovered method of the provided service.
* @param service the service to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingSelector
*/
public B filter(Object service) {
return filter(service, null);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector} for the
* method of the provided service.
* @param service the service to use.
* @param methodName the method to invoke
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingSelector
*/
public B filter(Object service, String methodName) {
return filter(service, methodName, null);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector} for the
* method of the provided service.
* @param service the service to use.
* @param methodName the method to invoke
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingSelector
*/
public B filter(Object service, String methodName, Consumer<FilterEndpointSpec> endpointConfigurer) {
MethodInvokingSelector selector;
if (StringUtils.hasText(methodName)) {
selector = new MethodInvokingSelector(service, methodName);
}
else {
selector = new MethodInvokingSelector(service);
}
return filter(selector, endpointConfigurer);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter("World"::equals)
* }
* </pre>
* @param genericSelector the {@link GenericSelector} to use.
* @param <P> the source payload type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <P> B filter(GenericSelector<P> genericSelector) {
return filter(null, genericSelector);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the {@link org.springframework.integration.handler.MessageProcessor} from
* the provided {@link MessageProcessorSpec}.
* <pre class="code">
* {@code
* .filter(Scripts.script(scriptResource).lang("ruby"))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B filter(MessageProcessorSpec<?> messageProcessorSpec) {
return filter(messageProcessorSpec, (Consumer<FilterEndpointSpec>) null);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the {@link org.springframework.integration.handler.MessageProcessor} from
* the provided {@link MessageProcessorSpec}.
* In addition accept options for the integration endpoint using {@link FilterEndpointSpec}.
* <pre class="code">
* {@code
* .filter(Scripts.script(scriptResource).lang("ruby"),
* e -> e.autoStartup(false))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B filter(MessageProcessorSpec<?> messageProcessorSpec, Consumer<FilterEndpointSpec> endpointConfigurer) {
Assert.notNull(messageProcessorSpec, "'messageProcessorSpec' must not be null");
MessageProcessor<?> processor = messageProcessorSpec.get();
return addComponent(processor)
.filter(new MethodInvokingSelector(processor), endpointConfigurer);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter(Date.class, p -> p.after(new Date()))
* }
* </pre>
* @param payloadType the {@link Class} for desired {@code payload} type.
* @param genericSelector the {@link GenericSelector} to use.
* @param <P> the source payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B filter(Class<P> payloadType, GenericSelector<P> genericSelector) {
return this.filter(payloadType, genericSelector, null);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* In addition accept options for the integration endpoint using {@link FilterEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter("World"::equals, e -> e.autoStartup(false))
* }
* </pre>
* @param genericSelector the {@link GenericSelector} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the source payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see FilterEndpointSpec
*/
public <P> B filter(GenericSelector<P> genericSelector, Consumer<FilterEndpointSpec> endpointConfigurer) {
return filter(null, genericSelector, endpointConfigurer);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* In addition accept options for the integration endpoint using {@link FilterEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter(Date.class, p -> p.after(new Date()), e -> e.autoStartup(false))
* }
* </pre>
* @param payloadType the {@link Class} for desired {@code payload} type.
* @param genericSelector the {@link GenericSelector} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the source payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
* @see FilterEndpointSpec
*/
public <P> B filter(Class<P> payloadType, GenericSelector<P> genericSelector,
Consumer<FilterEndpointSpec> endpointConfigurer) {
Assert.notNull(genericSelector, "'genericSelector' must not be null");
MessageSelector selector = genericSelector instanceof MessageSelector ? (MessageSelector) genericSelector :
(isLambda(genericSelector)
? new MethodInvokingSelector(new LambdaMessageProcessor(genericSelector, payloadType))
: new MethodInvokingSelector(genericSelector));
return this.register(new FilterEndpointSpec(new MessageFilter(selector)), endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the selected protocol specific
* {@link MessageHandler} implementation from {@code Namespace Factory}:
* <pre class="code">
* {@code
* .handle(Amqp.outboundAdapter(this.amqpTemplate).routingKeyExpression("headers.routingKey"))
* }
* </pre>
* @param messageHandlerSpec the {@link MessageHandlerSpec} to configure protocol specific
* {@link MessageHandler}.
* @param <H> the target {@link MessageHandler} type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <H extends MessageHandler> B handle(MessageHandlerSpec<?, H> messageHandlerSpec) {
return handle(messageHandlerSpec, (Consumer<GenericEndpointSpec<H>>) null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the provided
* {@link MessageHandler} implementation.
* Can be used as Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(m -> logger.info(m.getPayload())
* }
* </pre>
* @param messageHandler the {@link MessageHandler} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(MessageHandler messageHandler) {
return handle(messageHandler, (Consumer<GenericEndpointSpec<MessageHandler>>) null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the {@code method} for provided {@code bean} at runtime.
* @param beanName the bean name to use.
* @param methodName the method to invoke.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(String beanName, String methodName) {
return this.handle(beanName, methodName, null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the {@code method} for provided {@code bean} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param beanName the bean name to use.
* @param methodName the method to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(String beanName, String methodName,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
return handle(new ServiceActivatingHandler(new BeanNameMessageProcessor<Object>(beanName, methodName)),
endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link MethodInvokingMessageProcessor}
* to invoke the discovered {@code method} for provided {@code service} at runtime.
* @param service the service object to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(Object service) {
return handle(service, null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link MethodInvokingMessageProcessor}
* to invoke the {@code method} for provided {@code bean} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param service the service object to use.
* @param methodName the method to invoke.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(Object service, String methodName) {
return handle(service, methodName, null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link MethodInvokingMessageProcessor}
* to invoke the {@code method} for provided {@code bean} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param service the service object to use.
* @param methodName the method to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(Object service, String methodName,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
ServiceActivatingHandler handler;
if (StringUtils.hasText(methodName)) {
handler = new ServiceActivatingHandler(service, methodName);
}
else {
handler = new ServiceActivatingHandler(service);
}
return handle(handler, endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the provided {@link GenericHandler} at runtime.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .<Integer>handle((p, h) -> p / 2)
* }
* </pre>
* @param handler the handler to invoke.
* @param <P> the payload type to expect.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B handle(GenericHandler<P> handler) {
return handle(null, handler);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the provided {@link GenericHandler} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .<Integer>handle((p, h) -> p / 2, e -> e.autoStartup(false))
* }
* </pre>
* @param handler the handler to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type to expect.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
* @see GenericEndpointSpec
*/
public <P> B handle(GenericHandler<P> handler,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
return this.handle(null, handler, endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the provided {@link GenericHandler} at runtime.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(Integer.class, (p, h) -> p / 2)
* }
* </pre>
* @param payloadType the expected payload type.
* The accepted payload can be converted to this one at runtime
* @param handler the handler to invoke.
* @param <P> the payload type to expect.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B handle(Class<P> payloadType, GenericHandler<P> handler) {
return this.handle(payloadType, handler, null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the provided {@link GenericHandler} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(Integer.class, (p, h) -> p / 2, e -> e.autoStartup(false))
* }
* </pre>
* @param payloadType the expected payload type.
* The accepted payload can be converted to this one at runtime
* @param handler the handler to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type to expect.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B handle(Class<P> payloadType, GenericHandler<P> handler,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
ServiceActivatingHandler serviceActivatingHandler = null;
if (isLambda(handler)) {
serviceActivatingHandler = new ServiceActivatingHandler(new LambdaMessageProcessor(handler, payloadType));
}
else {
serviceActivatingHandler = new ServiceActivatingHandler(handler, "handle");
}
return this.handle(serviceActivatingHandler, endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MessageProcessor} from the provided
* {@link MessageProcessorSpec}.
* <pre class="code">
* {@code
* .handle(Scripts.script("classpath:myScript.ruby"))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(MessageProcessorSpec<?> messageProcessorSpec) {
return handle(messageProcessorSpec, (Consumer<GenericEndpointSpec<ServiceActivatingHandler>>) null);
}
/**
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MessageProcessor} from the provided
* {@link MessageProcessorSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* <pre class="code">
* {@code
* .handle(Scripts.script("classpath:myScript.ruby"), e -> e.autoStartup(false))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B handle(MessageProcessorSpec<?> messageProcessorSpec,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
Assert.notNull(messageProcessorSpec, "'messageProcessorSpec' must not be null");
MessageProcessor<?> processor = messageProcessorSpec.get();
return addComponent(processor)
.handle(new ServiceActivatingHandler(processor), endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the selected protocol specific
* {@link MessageHandler} implementation from {@code Namespace Factory}:
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(Amqp.outboundAdapter(this.amqpTemplate).routingKeyExpression("headers.routingKey"),
* e -> e.autoStartup(false))
* }
* </pre>
* @param messageHandlerSpec the {@link MessageHandlerSpec} to configure protocol specific
* {@link MessageHandler}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <H> the {@link MessageHandler} type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <H extends MessageHandler> B handle(MessageHandlerSpec<?, H> messageHandlerSpec,
Consumer<GenericEndpointSpec<H>> endpointConfigurer) {
Assert.notNull(messageHandlerSpec, "'messageHandlerSpec' must not be null");
if (messageHandlerSpec instanceof ComponentsRegistration) {
addComponents(((ComponentsRegistration) messageHandlerSpec).getComponentsToRegister());
}
return handle(messageHandlerSpec.get(), endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} for the provided
* {@link MessageHandler} implementation.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Can be used as Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(m -> logger.info(m.getPayload()), e -> e.autoStartup(false))
* }
* </pre>
* @param messageHandler the {@link MessageHandler} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <H> the {@link MessageHandler} type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <H extends MessageHandler> B handle(H messageHandler, Consumer<GenericEndpointSpec<H>> endpointConfigurer) {
Assert.notNull(messageHandler, "'messageHandler' must not be null");
return this.register(new GenericEndpointSpec<>(messageHandler), endpointConfigurer);
}
/**
* Populate a {@link BridgeHandler} to the current integration flow position.
* @return the current {@link IntegrationFlowDefinition}.
* @see #bridge(Consumer)
*/
public B bridge() {
return bridge(null);
}
/**
* Populate a {@link BridgeHandler} to the current integration flow position.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .bridge(s -> s.poller(Pollers.fixedDelay(100))
* .autoStartup(false)
* .id("priorityChannelBridge"))
* }
* </pre>
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
*/
public B bridge(Consumer<GenericEndpointSpec<BridgeHandler>> endpointConfigurer) {
return register(new GenericEndpointSpec<>(new BridgeHandler()), endpointConfigurer);
}
/**
* Populate a {@link DelayHandler} to the current integration flow position
* with default options.
* @param groupId the {@code groupId} for delayed messages in the
* {@link org.springframework.integration.store.MessageGroupStore}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B delay(String groupId) {
return this.delay(groupId, null);
}
/**
* Populate a {@link DelayHandler} to the current integration flow position.
* @param groupId the {@code groupId} for delayed messages in the
* {@link org.springframework.integration.store.MessageGroupStore}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see DelayerEndpointSpec
*/
public B delay(String groupId, Consumer<DelayerEndpointSpec> endpointConfigurer) {
return register(new DelayerEndpointSpec(new DelayHandler(groupId)), endpointConfigurer);
}
/**
* Populate a {@link ContentEnricher} to the current integration flow position
* with provided options.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .enrich(e -> e.requestChannel("enrichChannel")
* .requestPayload(Message::getPayload)
* .shouldClonePayload(false)
* .autoStartup(false)
* .<Map<String, String>>headerFunction("foo", m -> m.getPayload().get("name")))
* }
* </pre>
* @param enricherConfigurer the {@link Consumer} to provide {@link ContentEnricher} options.
* @return the current {@link IntegrationFlowDefinition}.
* @see EnricherSpec
*/
public B enrich(Consumer<EnricherSpec> enricherConfigurer) {
return register(new EnricherSpec(), enricherConfigurer);
}
/**
* Populate a {@link MessageTransformingHandler} for
* a {@link org.springframework.integration.transformer.HeaderEnricher}
* using header values from provided {@link MapBuilder}.
* Can be used together with {@code Namespace Factory}:
* <pre class="code">
* {@code
* .enrichHeaders(Mail.headers()
* .subjectFunction(m -> "foo")
* .from("foo@bar")
* .toFunction(m -> new String[] {"bar@baz"}))
* }
* </pre>
* @param headers the {@link MapBuilder} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B enrichHeaders(MapBuilder<?, String, Object> headers) {
return enrichHeaders(headers, null);
}
/**
* Populate a {@link MessageTransformingHandler} for
* a {@link org.springframework.integration.transformer.HeaderEnricher}
* using header values from provided {@link MapBuilder}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Can be used together with {@code Namespace Factory}:
* <pre class="code">
* {@code
* .enrichHeaders(Mail.headers()
* .subjectFunction(m -> "foo")
* .from("foo@bar")
* .toFunction(m -> new String[] {"bar@baz"}),
* e -> e.autoStartup(false))
* }
* </pre>
* @param headers the {@link MapBuilder} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
*/
public B enrichHeaders(MapBuilder<?, String, Object> headers,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
return enrichHeaders(headers.get(), endpointConfigurer);
}
/**
* Accept a {@link Map} of values to be used for the
* {@link org.springframework.messaging.Message} header enrichment.
* {@code values} can apply an {@link org.springframework.expression.Expression}
* to be evaluated against a request {@link org.springframework.messaging.Message}.
* @param headers the Map of headers to enrich.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B enrichHeaders(Map<String, Object> headers) {
return enrichHeaders(headers, null);
}
/**
* Accept a {@link Map} of values to be used for the
* {@link org.springframework.messaging.Message} header enrichment.
* {@code values} can apply an {@link org.springframework.expression.Expression}
* to be evaluated against a request {@link org.springframework.messaging.Message}.
* @param headers the Map of headers to enrich.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
*/
public B enrichHeaders(final Map<String, Object> headers,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
HeaderEnricherSpec headerEnricherSpec = new HeaderEnricherSpec();
headerEnricherSpec.headers(headers);
Tuple2<ConsumerEndpointFactoryBean, MessageTransformingHandler> tuple2 = headerEnricherSpec.get();
return addComponents(headerEnricherSpec.getComponentsToRegister())
.handle(tuple2.getT2(), endpointConfigurer);
}
/**
* Populate a {@link MessageTransformingHandler} for
* a {@link org.springframework.integration.transformer.HeaderEnricher}
* as the result of provided {@link Consumer}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .enrichHeaders(h -> h.header(FileHeaders.FILENAME, "foo.sitest")
* .header("directory", new File(tmpDir, "fileWritingFlow")))
* }
* </pre>
* @param headerEnricherConfigurer the {@link Consumer} to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see HeaderEnricherSpec
*/
public B enrichHeaders(Consumer<HeaderEnricherSpec> headerEnricherConfigurer) {
Assert.notNull(headerEnricherConfigurer, "'headerEnricherConfigurer' must not be null");
return register(new HeaderEnricherSpec(), headerEnricherConfigurer);
}
/**
* Populate the {@link DefaultMessageSplitter} with default options
* to the current integration flow position.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B split() {
return this.split((Consumer<SplitterEndpointSpec<DefaultMessageSplitter>>) null);
}
/**
* Populate the {@link DefaultMessageSplitter} with provided options
* to the current integration flow position.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .split(s -> s.applySequence(false).get().getT2().setDelimiters(","))
* }
* </pre>
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options
* and for {@link DefaultMessageSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(Consumer<SplitterEndpointSpec<DefaultMessageSplitter>> endpointConfigurer) {
return this.split(new DefaultMessageSplitter(), endpointConfigurer);
}
/**
* Populate the {@link ExpressionEvaluatingSplitter} with provided
* SpEL expression.
* @param expression the splitter SpEL expression.
* and for {@link ExpressionEvaluatingSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(String expression) {
return split(expression, (Consumer<SplitterEndpointSpec<ExpressionEvaluatingSplitter>>) null);
}
/**
* Populate the {@link ExpressionEvaluatingSplitter} with provided
* SpEL expression.
* @param expression the splitter SpEL expression.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options
* and for {@link ExpressionEvaluatingSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(String expression, Consumer<SplitterEndpointSpec<ExpressionEvaluatingSplitter>> endpointConfigurer) {
Assert.hasText(expression, "'expression' must not be empty");
return split(new ExpressionEvaluatingSplitter(PARSER.parseExpression(expression)), endpointConfigurer);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the discovered
* {@code method} of the {@code service} at runtime.
* @param service the service to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingSplitter
*/
public B split(Object service) {
return split(service, null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@code method} of the {@code service} at runtime.
* @param service the service to use.
* @param methodName the method to invoke.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingSplitter
*/
public B split(Object service, String methodName) {
return split(service, methodName, null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@code method} of the {@code bean} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param service the service to use.
* @param methodName the method to invoke.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options
* and for {@link MethodInvokingSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
* @see MethodInvokingSplitter
*/
public B split(Object service, String methodName,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
MethodInvokingSplitter splitter;
if (StringUtils.hasText(methodName)) {
splitter = new MethodInvokingSplitter(service, methodName);
}
else {
splitter = new MethodInvokingSplitter(service);
}
return split(splitter, endpointConfigurer);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@code method} of the {@code bean} at runtime.
* @param beanName the bean name to use.
* @param methodName the method to invoke at runtime.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B split(String beanName, String methodName) {
return this.split(beanName, methodName, null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@code method} of the {@code bean} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param beanName the bean name to use.
* @param methodName the method to invoke at runtime.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options
* and for {@link MethodInvokingSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(String beanName, String methodName,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
return split(new MethodInvokingSplitter(new BeanNameMessageProcessor<Object>(beanName, methodName)),
endpointConfigurer);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the
* {@link org.springframework.integration.handler.MessageProcessor} at runtime
* from provided {@link MessageProcessorSpec}.
* <pre class="code">
* {@code
* .split(Scripts.script("classpath:myScript.ruby"))
* }
* </pre>
* @param messageProcessorSpec the splitter {@link MessageProcessorSpec}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(MessageProcessorSpec<?> messageProcessorSpec) {
return split(messageProcessorSpec, (Consumer<SplitterEndpointSpec<MethodInvokingSplitter>>) null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the
* {@link org.springframework.integration.handler.MessageProcessor} at runtime
* from provided {@link MessageProcessorSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* <pre class="code">
* {@code
* .split(Scripts.script(myScriptResource).lang("groovy").refreshCheckDelay(1000),
* , e -> e.applySequence(false))
* }
* </pre>
* @param messageProcessorSpec the splitter {@link MessageProcessorSpec}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options
* and for {@link MethodInvokingSplitter}.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(MessageProcessorSpec<?> messageProcessorSpec,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
Assert.notNull(messageProcessorSpec, "'messageProcessorSpec' must not be null");
MessageProcessor<?> processor = messageProcessorSpec.get();
return addComponent(processor)
.split(new MethodInvokingSplitter(processor), endpointConfigurer);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@link Function} at runtime.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .split(String.class, p ->
* jdbcTemplate.execute("SELECT * from FOO",
* (PreparedStatement ps) ->
* new ResultSetIterator<Foo>(ps.executeQuery(),
* (rs, rowNum) ->
* new Foo(rs.getInt(1), rs.getString(2)))))
* }
* </pre>
* @param payloadType the expected payload type. Used at runtime to convert received payload type to.
* @param splitter the splitter {@link Function}.
* @param <P> the payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B split(Class<P> payloadType, Function<P, ?> splitter) {
return split(payloadType, splitter, null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@link Function} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .<String>split(p ->
* jdbcTemplate.execute("SELECT * from FOO",
* (PreparedStatement ps) ->
* new ResultSetIterator<Foo>(ps.executeQuery(),
* (rs, rowNum) ->
* new Foo(rs.getInt(1), rs.getString(2))))
* , e -> e.applySequence(false))
* }
* </pre>
* @param splitter the splitter {@link Function}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
* @see SplitterEndpointSpec
*/
public <P> B split(Function<P, ?> splitter,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
return split(null, splitter, endpointConfigurer);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@link Function} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .split(String.class, p ->
* jdbcTemplate.execute("SELECT * from FOO",
* (PreparedStatement ps) ->
* new ResultSetIterator<Foo>(ps.executeQuery(),
* (rs, rowNum) ->
* new Foo(rs.getInt(1), rs.getString(2))))
* , e -> e.applySequence(false))
* }
* </pre>
* @param payloadType the expected payload type. Used at runtime to convert received payload type to.
* @param splitter the splitter {@link Function}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
* @see SplitterEndpointSpec
*/
public <P> B split(Class<P> payloadType, Function<P, ?> splitter,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
MethodInvokingSplitter split = isLambda(splitter)
? new MethodInvokingSplitter(new LambdaMessageProcessor(splitter, payloadType))
: new MethodInvokingSplitter(splitter);
return this.split(split, endpointConfigurer);
}
/**
* Populate the provided {@link AbstractMessageSplitter} to the current integration
* flow position.
* @param splitterMessageHandlerSpec the {@link MessageHandlerSpec} to populate.
* @param <S> the {@link AbstractMessageSplitter}
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public <S extends AbstractMessageSplitter> B split(MessageHandlerSpec<?, S> splitterMessageHandlerSpec) {
return split(splitterMessageHandlerSpec, (Consumer<SplitterEndpointSpec<S>>) null);
}
/**
* Populate the provided {@link AbstractMessageSplitter} to the current integration
* flow position.
* @param splitterMessageHandlerSpec the {@link MessageHandlerSpec} to populate.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <S> the {@link AbstractMessageSplitter}
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public <S extends AbstractMessageSplitter> B split(MessageHandlerSpec<?, S> splitterMessageHandlerSpec,
Consumer<SplitterEndpointSpec<S>> endpointConfigurer) {
Assert.notNull(splitterMessageHandlerSpec, "'splitterMessageHandlerSpec' must not be null");
return split(splitterMessageHandlerSpec.get(), endpointConfigurer);
}
/**
* Populate the provided {@link AbstractMessageSplitter} to the current integration
* flow position.
* @param splitter the {@link AbstractMessageSplitter} to populate.
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public B split(AbstractMessageSplitter splitter) {
return split(splitter, (Consumer<SplitterEndpointSpec<AbstractMessageSplitter>>) null);
}
/**
* Populate the provided {@link AbstractMessageSplitter} to the current integration
* flow position.
* @param splitter the {@link AbstractMessageSplitter} to populate.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <S> the {@link AbstractMessageSplitter}
* @return the current {@link IntegrationFlowDefinition}.
* @see SplitterEndpointSpec
*/
public <S extends AbstractMessageSplitter> B split(S splitter,
Consumer<SplitterEndpointSpec<S>> endpointConfigurer) {
Assert.notNull(splitter, "'splitter' must not be null");
return this.register(new SplitterEndpointSpec<>(splitter), endpointConfigurer);
}
/**
* Provide the {@link HeaderFilter} to the current {@link StandardIntegrationFlow}.
* @param headersToRemove the array of headers (or patterns)
* to remove from {@link org.springframework.messaging.MessageHeaders}.
* @return this {@link IntegrationFlowDefinition}.
*/
public B headerFilter(String... headersToRemove) {
return this.headerFilter(new HeaderFilter(headersToRemove), null);
}
/**
* Provide the {@link HeaderFilter} to the current {@link StandardIntegrationFlow}.
* @param headersToRemove the comma separated headers (or patterns) to remove from
* {@link org.springframework.messaging.MessageHeaders}.
* @param patternMatch the {@code boolean} flag to indicate if {@code headersToRemove}
* should be interpreted as patterns or direct header names.
* @return this {@link IntegrationFlowDefinition}.
*/
public B headerFilter(String headersToRemove, boolean patternMatch) {
HeaderFilter headerFilter = new HeaderFilter(StringUtils.delimitedListToStringArray(headersToRemove, ",", " "));
headerFilter.setPatternMatch(patternMatch);
return this.headerFilter(headerFilter, null);
}
/**
* Populate the provided {@link MessageTransformingHandler} for the provided
* {@link HeaderFilter}.
* @param headerFilter the {@link HeaderFilter} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
*/
public B headerFilter(HeaderFilter headerFilter,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
return this.transform(headerFilter, endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} for the {@link ClaimCheckInTransformer}
* with provided {@link MessageStore}.
* @param messageStore the {@link MessageStore} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B claimCheckIn(MessageStore messageStore) {
return this.claimCheckIn(messageStore, null);
}
/**
* Populate the {@link MessageTransformingHandler} for the {@link ClaimCheckInTransformer}
* with provided {@link MessageStore}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param messageStore the {@link MessageStore} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
*/
public B claimCheckIn(MessageStore messageStore,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
return this.transform(new ClaimCheckInTransformer(messageStore), endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} for the {@link ClaimCheckOutTransformer}
* with provided {@link MessageStore}.
* The {@code removeMessage} option of {@link ClaimCheckOutTransformer} is to {@code false}.
* @param messageStore the {@link MessageStore} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B claimCheckOut(MessageStore messageStore) {
return this.claimCheckOut(messageStore, false);
}
/**
* Populate the {@link MessageTransformingHandler} for the {@link ClaimCheckOutTransformer}
* with provided {@link MessageStore} and {@code removeMessage} flag.
* @param messageStore the {@link MessageStore} to use.
* @param removeMessage the removeMessage boolean flag.
* @return the current {@link IntegrationFlowDefinition}.
* @see ClaimCheckOutTransformer#setRemoveMessage(boolean)
*/
public B claimCheckOut(MessageStore messageStore, boolean removeMessage) {
return this.claimCheckOut(messageStore, removeMessage, null);
}
/**
* Populate the {@link MessageTransformingHandler} for the {@link ClaimCheckOutTransformer}
* with provided {@link MessageStore} and {@code removeMessage} flag.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param messageStore the {@link MessageStore} to use.
* @param removeMessage the removeMessage boolean flag.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
* @see GenericEndpointSpec
* @see ClaimCheckOutTransformer#setRemoveMessage(boolean)
*/
public B claimCheckOut(MessageStore messageStore, boolean removeMessage,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
ClaimCheckOutTransformer claimCheckOutTransformer = new ClaimCheckOutTransformer(messageStore);
claimCheckOutTransformer.setRemoveMessage(removeMessage);
return this.transform(claimCheckOutTransformer, endpointConfigurer);
}
/**
* Populate the
* {@link org.springframework.integration.aggregator.ResequencingMessageHandler} with
* default options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B resequence() {
return resequence(null);
}
/**
* Populate the
* {@link org.springframework.integration.aggregator.ResequencingMessageHandler} with
* provided options from {@link ResequencerSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .resequence(r -> r.releasePartialSequences(true)
* .correlationExpression("'foo'")
* .phase(100))
* }
* </pre>
* @param resequencer the {@link Consumer} to provide
* {@link org.springframework.integration.aggregator.ResequencingMessageHandler} options.
* @return the current {@link IntegrationFlowDefinition}.
* @see ResequencerSpec
*/
public B resequence(Consumer<ResequencerSpec> resequencer) {
return register(new ResequencerSpec(), resequencer);
}
/**
* Populate the {@link AggregatingMessageHandler} with default options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B aggregate() {
return aggregate(null);
}
/**
* Populate the {@link AggregatingMessageHandler} with provided options from {@link AggregatorSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .aggregate(a -> a.correlationExpression("1")
* .releaseStrategy(g -> g.size() == 25)
* .phase(100))
* }
* </pre>
* @param aggregator the {@link Consumer} to provide {@link AggregatingMessageHandler} options.
* @return the current {@link IntegrationFlowDefinition}.
* @see AggregatorSpec
*/
public B aggregate(Consumer<AggregatorSpec> aggregator) {
return register(new AggregatorSpec(), aggregator);
}
/**
* Populate the {@link MethodInvokingRouter} for provided bean and its method
* with default options.
* @param beanName the bean to use.
* @param method the method to invoke at runtime.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(String beanName, String method) {
return route(beanName, method, null);
}
/**
* Populate the {@link MethodInvokingRouter} for provided bean and its method
* with provided options from {@link RouterSpec}.
* @param beanName the bean to use.
* @param method the method to invoke at runtime.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(String beanName, String method, Consumer<RouterSpec<Object,
MethodInvokingRouter>> routerConfigurer) {
MethodInvokingRouter methodInvokingRouter =
new MethodInvokingRouter(new BeanNameMessageProcessor<>(beanName, method));
return route(new RouterSpec<>(methodInvokingRouter), routerConfigurer);
}
/**
* Populate the {@link MethodInvokingRouter} for the discovered method
* of the provided service and its method with default options.
* @param service the bean to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingRouter
*/
public B route(Object service) {
return route(service, null);
}
/**
* Populate the {@link MethodInvokingRouter} for the method
* of the provided service and its method with default options.
* @param service the service to use.
* @param methodName the method to invoke.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingRouter
*/
public B route(Object service, String methodName) {
return route(service, methodName, null);
}
/**
* Populate the {@link MethodInvokingRouter} for the method
* of the provided service and its method with provided options from {@link RouterSpec}.
* @param service the service to use.
* @param methodName the method to invoke.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
* @return the current {@link IntegrationFlowDefinition}.
* @see MethodInvokingRouter
*/
public B route(Object service, String methodName,
Consumer<RouterSpec<Object, MethodInvokingRouter>> routerConfigurer) {
MethodInvokingRouter router;
if (StringUtils.hasText(methodName)) {
router = new MethodInvokingRouter(service, methodName);
}
else {
router = new MethodInvokingRouter(service);
}
return route(new RouterSpec<>(router), routerConfigurer);
}
/**
* Populate the {@link ExpressionEvaluatingRouter} for provided SpEL expression
* with default options.
* @param expression the expression to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(String expression) {
return route(expression, (Consumer<RouterSpec<Object, ExpressionEvaluatingRouter>>) null);
}
/**
* Populate the {@link ExpressionEvaluatingRouter} for provided SpEL expression
* with provided options from {@link RouterSpec}.
* @param expression the expression to use.
* @param routerConfigurer the {@link Consumer} to provide {@link ExpressionEvaluatingRouter} options.
* @param <T> the target result type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <T> B route(String expression, Consumer<RouterSpec<T, ExpressionEvaluatingRouter>> routerConfigurer) {
return route(new RouterSpec<>(new ExpressionEvaluatingRouter(PARSER.parseExpression(expression))),
routerConfigurer);
}
/**
* Populate the {@link MethodInvokingRouter} for provided {@link Function}
* with default options.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .route(p -> p.equals("foo") || p.equals("bar") ? new String[] {"foo", "bar"} : null)
* }
* </pre>
* @param router the {@link Function} to use.
* @param <S> the source payload type.
* @param <T> the target result type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <S, T> B route(Function<S, T> router) {
return route(null, router);
}
/**
* Populate the {@link MethodInvokingRouter} for provided {@link Function}
* and payload type with default options.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .route(Integer.class, p -> p % 2 == 0)
* }
* </pre>
* @param payloadType the expected payload type.
* @param router the {@link Function} to use.
* @param <S> the source payload type.
* @param <T> the target result type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <S, T> B route(Class<S> payloadType, Function<S, T> router) {
return route(payloadType, router, null);
}
/**
* Populate the {@link MethodInvokingRouter} for provided {@link Function}
* with provided options from {@link RouterSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .<Integer, Boolean>route(p -> p % 2 == 0,
* m -> m.channelMapping("true", "evenChannel")
* .subFlowMapping("false", f ->
* f.<Integer>handle((p, h) -> p * 3))
* .applySequence(false))
* }
* </pre>
* @param router the {@link Function} to use.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
* @param <S> the source payload type.
* @param <T> the target result type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <S, T> B route(Function<S, T> router, Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer) {
return route(null, router, routerConfigurer);
}
/**
* Populate the {@link MethodInvokingRouter} for provided {@link Function}
* and payload type and options from {@link RouterSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .route(Integer.class, p -> p % 2 == 0,
* m -> m.channelMapping("true", "evenChannel")
* .subFlowMapping("false", f ->
* f.<Integer>handle((p, h) -> p * 3))
* .applySequence(false))
* }
* </pre>
* @param payloadType the expected payload type.
* @param router the {@link Function} to use.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
* @param <P> the source payload type.
* @param <T> the target result type.
* @return the current {@link IntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P, T> B route(Class<P> payloadType, Function<P, T> router,
Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer) {
MethodInvokingRouter methodInvokingRouter = isLambda(router)
? new MethodInvokingRouter(new LambdaMessageProcessor(router, payloadType))
: new MethodInvokingRouter(router);
return route(new RouterSpec<>(methodInvokingRouter), routerConfigurer);
}
/**
* Populate the {@link MethodInvokingRouter} for the {@link org.springframework.integration.handler.MessageProcessor}
* from the provided {@link MessageProcessorSpec} with default options.
* <pre class="code">
* {@code
* .route(Scripts.script(myScriptResource).lang("groovy").refreshCheckDelay(1000))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(MessageProcessorSpec<?> messageProcessorSpec) {
return route(messageProcessorSpec, (Consumer<RouterSpec<Object, MethodInvokingRouter>>) null);
}
/**
* Populate the {@link MethodInvokingRouter} for the {@link org.springframework.integration.handler.MessageProcessor}
* from the provided {@link MessageProcessorSpec} with default options.
* <pre class="code">
* {@code
* .route(Scripts.script(myScriptResource).lang("groovy").refreshCheckDelay(1000),
* m -> m.channelMapping("true", "evenChannel")
* .subFlowMapping("false", f ->
* f.<Integer>handle((p, h) -> p * 3)))
* }
* </pre>
* @param messageProcessorSpec the {@link MessageProcessorSpec} to use.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(MessageProcessorSpec<?> messageProcessorSpec,
Consumer<RouterSpec<Object, MethodInvokingRouter>> routerConfigurer) {
Assert.notNull(messageProcessorSpec, "'messageProcessorSpec' must not be null");
MessageProcessor<?> processor = messageProcessorSpec.get();
addComponent(processor);
return route(new RouterSpec<>(new MethodInvokingRouter(processor)), routerConfigurer);
}
private <R extends AbstractMessageRouter, S extends AbstractRouterSpec<S, R>> B route(S routerSpec,
Consumer<S> routerConfigurer) {
if (routerConfigurer != null) {
routerConfigurer.accept(routerSpec);
}
BridgeHandler bridgeHandler = new BridgeHandler();
boolean registerSubflowBridge = false;
Collection<Object> componentsToRegister = routerSpec.getComponentsToRegister();
if (!CollectionUtils.isEmpty(componentsToRegister)) {
for (Object component : componentsToRegister) {
if (component instanceof IntegrationFlowDefinition) {
IntegrationFlowDefinition<?> flowBuilder = (IntegrationFlowDefinition<?>) component;
if (flowBuilder.isOutputChannelRequired()) {
registerSubflowBridge = true;
flowBuilder.channel(new FixedSubscriberChannel(bridgeHandler));
}
addComponent(flowBuilder.get());
}
else {
addComponent(component);
}
}
}
if (componentsToRegister != null) {
componentsToRegister.clear();
}
register(routerSpec, null);
if (routerSpec.isDefaultToParentFlow()) {
routerSpec.defaultOutputChannel(new FixedSubscriberChannel(bridgeHandler));
registerSubflowBridge = true;
}
if (registerSubflowBridge) {
this.currentComponent = null;
handle(bridgeHandler);
}
return _this();
}
/**
* Populate the {@link RecipientListRouter} with options from the {@link RecipientListRouterSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .routeToRecipients(r -> r
* .recipient("bar-channel", m ->
* m.getHeaders().containsKey("recipient") && (boolean) m.getHeaders().get("recipient"))
* .recipientFlow("'foo' == payload or 'bar' == payload or 'baz' == payload",
* f -> f.transform(String.class, p -> p.toUpperCase())
* .channel(c -> c.queue("recipientListSubFlow1Result"))))
* }
* </pre>
* @param routerConfigurer the {@link Consumer} to provide {@link RecipientListRouter} options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B routeToRecipients(Consumer<RecipientListRouterSpec> routerConfigurer) {
return route(new RecipientListRouterSpec(), routerConfigurer);
}
/**
* Populate the {@link ErrorMessageExceptionTypeRouter} with options from the {@link RouterSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .routeByException(r -> r
* .channelMapping(IllegalArgumentException.class, "illegalArgumentChannel")
* .subFlowMapping(MessageHandlingException.class, sf ->
* sf.handle(...))
* )
* }
* </pre>
* @param routerConfigurer the {@link Consumer} to provide {@link ErrorMessageExceptionTypeRouter} options.
* @return the current {@link IntegrationFlowDefinition}.
* @see ErrorMessageExceptionTypeRouter
*/
public B routeByException(
Consumer<RouterSpec<Class<? extends Throwable>, ErrorMessageExceptionTypeRouter>> routerConfigurer) {
return route(new RouterSpec<>(new ErrorMessageExceptionTypeRouter()), routerConfigurer);
}
/**
* Populate the provided {@link AbstractMessageRouter} implementation to the
* current integration flow position.
* @param router the {@link AbstractMessageRouter} to populate.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B route(AbstractMessageRouter router) {
return route(router, (Consumer<GenericEndpointSpec<AbstractMessageRouter>>) null);
}
/**
* Populate the provided {@link AbstractMessageRouter} implementation to the
* current integration flow position.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param router the {@link AbstractMessageRouter} to populate.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <R> the {@link AbstractMessageRouter} type.
* @return the current {@link IntegrationFlowDefinition}.
*/
public <R extends AbstractMessageRouter> B route(R router, Consumer<GenericEndpointSpec<R>> endpointConfigurer) {
return handle(router, endpointConfigurer);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler} for the
* provided {@code requestChannel} to send a request with default options.
* Uses {@link org.springframework.integration.gateway.RequestReplyExchanger} Proxy
* on the background.
* @param requestChannel the {@link MessageChannel} bean name.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(String requestChannel) {
return gateway(requestChannel, null);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler} for the
* provided {@code requestChannel} to send a request with options from
* {@link GatewayEndpointSpec}. Uses
* {@link org.springframework.integration.gateway.RequestReplyExchanger} Proxy on the
* background.
* @param requestChannel the {@link MessageChannel} bean name.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint
* options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(String requestChannel, Consumer<GatewayEndpointSpec> endpointConfigurer) {
return register(new GatewayEndpointSpec(requestChannel), endpointConfigurer);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler}
* for the provided {@code requestChannel} to send a request with default options.
* Uses {@link org.springframework.integration.gateway.RequestReplyExchanger} Proxy on
* the background.
* @param requestChannel the {@link MessageChannel} to use.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(MessageChannel requestChannel) {
return gateway(requestChannel, null);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler} for the
* provided {@code requestChannel} to send a request with options from
* {@link GatewayEndpointSpec}. Uses
* {@link org.springframework.integration.gateway.RequestReplyExchanger} Proxy on the
* background.
* @param requestChannel the {@link MessageChannel} to use.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint
* options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(MessageChannel requestChannel, Consumer<GatewayEndpointSpec> endpointConfigurer) {
return register(new GatewayEndpointSpec(requestChannel), endpointConfigurer);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler} for the
* provided {@code subflow}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .gateway(f -> f.transform("From Gateway SubFlow: "::concat))
* }
* </pre>
* @param flow the {@link IntegrationFlow} to to send a request message and wait for reply.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(IntegrationFlow flow) {
return gateway(flow, null);
}
/**
* Populate the "artificial"
* {@link org.springframework.integration.gateway.GatewayMessageHandler} for the
* provided {@code subflow} with options from {@link GatewayEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .gateway(f -> f.transform("From Gateway SubFlow: "::concat), e -> e.replyTimeout(100L))
* }
* </pre>
* @param flow the {@link IntegrationFlow} to to send a request message and wait for reply.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B gateway(IntegrationFlow flow, Consumer<GatewayEndpointSpec> endpointConfigurer) {
Assert.notNull(flow, "'flow' must not be null");
final DirectChannel requestChannel = new DirectChannel();
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(requestChannel);
flow.configure(flowBuilder);
addComponent(flowBuilder.get());
return gateway(requestChannel, endpointConfigurer);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the {@code INFO}
* logging level and {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category.
* <p> The full request {@link Message} will be logged.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log() {
return log(LoggingHandler.Level.INFO);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for provided {@link LoggingHandler.Level}
* logging level and {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category.
* <p> The full request {@link Message} will be logged.
* @param level the {@link LoggingHandler.Level}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(LoggingHandler.Level level) {
return log(level, (String) null);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided logging category
* and {@code INFO} logging level.
* <p> The full request {@link Message} will be logged.
* @param category the logging category to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(String category) {
return log(LoggingHandler.Level.INFO, category);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level and logging category.
* <p> The full request {@link Message} will be logged.
* @param level the {@link LoggingHandler.Level}.
* @param category the logging category to use.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(LoggingHandler.Level level, String category) {
return log(level, category, (Expression) null);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level, logging category
* and SpEL expression for the log message.
* @param level the {@link LoggingHandler.Level}.
* @param category the logging category.
* @param logExpression the SpEL expression to evaluate logger message at runtime
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(LoggingHandler.Level level, String category, String logExpression) {
Assert.hasText(logExpression, "'logExpression' must not be empty");
return log(level, category, PARSER.parseExpression(logExpression));
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the {@code INFO} logging level,
* the {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category and {@link Function} for the log message.
* @param function the function to evaluate logger message at runtime
* @param <P> the expected payload type.
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public <P> B log(Function<Message<P>, Object> function) {
Assert.notNull(function, "'function' must not be null");
return log(new FunctionExpression<>(function));
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the {@code INFO} logging level,
* the {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category and SpEL expression to evaluate
* logger message at runtime against the request {@link Message}.
* @param logExpression the {@link Expression} to evaluate logger message at runtime
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(Expression logExpression) {
return log(LoggingHandler.Level.INFO, logExpression);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level,
* the {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category and SpEL expression to evaluate
* logger message at runtime against the request {@link Message}.
* @param level the {@link LoggingHandler.Level}.
* @param logExpression the {@link Expression} to evaluate logger message at runtime
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(LoggingHandler.Level level, Expression logExpression) {
return log(level, null, logExpression);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the {@code INFO}
* {@link LoggingHandler.Level} logging level,
* the provided logging category and SpEL expression to evaluate
* logger message at runtime against the request {@link Message}.
* @param category the logging category.
* @param logExpression the {@link Expression} to evaluate logger message at runtime
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(String category, Expression logExpression) {
return log(LoggingHandler.Level.INFO, category, logExpression);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level,
* the {@code org.springframework.integration.handler.LoggingHandler}
* as a default logging category and {@link Function} for the log message.
* @param level the {@link LoggingHandler.Level}.
* @param function the function to evaluate logger message at runtime
* @param <P> the expected payload type.
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public <P> B log(LoggingHandler.Level level, Function<Message<P>, Object> function) {
return log(level, null, function);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level,
* the provided logging category and {@link Function} for the log message.
* @param category the logging category.
* @param function the function to evaluate logger message at runtime
* @param <P> the expected payload type.
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public <P> B log(String category, Function<Message<P>, Object> function) {
return log(LoggingHandler.Level.INFO, category, function);
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level, logging category
* and {@link Function} for the log message.
* @param level the {@link LoggingHandler.Level}.
* @param category the logging category.
* @param function the function to evaluate logger message at runtime
* @param <P> the expected payload type.
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public <P> B log(LoggingHandler.Level level, String category, Function<Message<P>, Object> function) {
Assert.notNull(function, "'function' must not be null");
return log(level, category, new FunctionExpression<>(function));
}
/**
* Populate a {@link WireTap} for the {@link #currentMessageChannel}
* with the {@link LoggingHandler} subscriber for the provided
* {@link LoggingHandler.Level} logging level, logging category
* and SpEL expression for the log message.
* @param level the {@link LoggingHandler.Level}.
* @param category the logging category.
* @param logExpression the {@link Expression} to evaluate logger message at runtime
* against the request {@link Message}.
* @return the current {@link IntegrationFlowDefinition}.
* @see #wireTap(WireTapSpec)
*/
public B log(LoggingHandler.Level level, String category, Expression logExpression) {
LoggingHandler loggingHandler = new LoggingHandler(level);
if (StringUtils.hasText(category)) {
loggingHandler.setLoggerName(category);
}
if (logExpression != null) {
loggingHandler.setLogExpression(logExpression);
}
else {
loggingHandler.setShouldLogFullMessage(true);
}
addComponent(loggingHandler);
MessageChannel loggerChannel = new FixedSubscriberChannel(loggingHandler);
return wireTap(loggerChannel);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link MessageChannel} for scattering function
* and default {@link AggregatorSpec} for gathering function.
* @param scatterChannel the {@link MessageChannel} for scatting requests.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(MessageChannel scatterChannel) {
return scatterGather(scatterChannel, null);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link MessageChannel} for scattering function
* and {@link AggregatorSpec} for gathering function.
* @param scatterChannel the {@link MessageChannel} for scatting requests.
* @param gatherer the {@link Consumer} for {@link AggregatorSpec} to configure gatherer.
* Can be {@code null}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(MessageChannel scatterChannel, Consumer<AggregatorSpec> gatherer) {
return scatterGather(scatterChannel, gatherer, null);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link MessageChannel} for scattering function
* and {@link AggregatorSpec} for gathering function.
* @param scatterChannel the {@link MessageChannel} for scatting requests.
* @param gatherer the {@link Consumer} for {@link AggregatorSpec} to configure gatherer.
* Can be {@code null}.
* @param scatterGather the {@link Consumer} for {@link ScatterGatherSpec} to configure
* {@link ScatterGatherHandler} and its endpoint. Can be {@code null}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(MessageChannel scatterChannel, Consumer<AggregatorSpec> gatherer,
Consumer<ScatterGatherSpec> scatterGather) {
AggregatorSpec aggregatorSpec = new AggregatorSpec();
if (gatherer != null) {
gatherer.accept(aggregatorSpec);
}
AggregatingMessageHandler aggregatingMessageHandler = aggregatorSpec.get().getT2();
addComponent(aggregatingMessageHandler);
ScatterGatherHandler messageHandler = new ScatterGatherHandler(scatterChannel, aggregatingMessageHandler);
return register(new ScatterGatherSpec(messageHandler), scatterGather);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link RecipientListRouterSpec} for scattering function
* and default {@link AggregatorSpec} for gathering function.
* @param scatterer the {@link Consumer} for {@link RecipientListRouterSpec} to configure scatterer.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(Consumer<RecipientListRouterSpec> scatterer) {
return scatterGather(scatterer, null);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link RecipientListRouterSpec} for scattering function
* and {@link AggregatorSpec} for gathering function.
* @param scatterer the {@link Consumer} for {@link RecipientListRouterSpec} to configure scatterer.
* Can be {@code null}.
* @param gatherer the {@link Consumer} for {@link AggregatorSpec} to configure gatherer.
* Can be {@code null}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(Consumer<RecipientListRouterSpec> scatterer, Consumer<AggregatorSpec> gatherer) {
return scatterGather(scatterer, gatherer, null);
}
/**
* Populate a {@link ScatterGatherHandler} to the current integration flow position
* based on the provided {@link RecipientListRouterSpec} for scattering function
* and {@link AggregatorSpec} for gathering function.
* @param scatterer the {@link Consumer} for {@link RecipientListRouterSpec} to configure scatterer.
* @param gatherer the {@link Consumer} for {@link AggregatorSpec} to configure gatherer.
* @param scatterGather the {@link Consumer} for {@link ScatterGatherSpec} to configure
* {@link ScatterGatherHandler} and its endpoint. Can be {@code null}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B scatterGather(Consumer<RecipientListRouterSpec> scatterer, Consumer<AggregatorSpec> gatherer,
Consumer<ScatterGatherSpec> scatterGather) {
Assert.notNull(scatterer, "'scatterer' must not be null");
RecipientListRouterSpec recipientListRouterSpec = new RecipientListRouterSpec();
scatterer.accept(recipientListRouterSpec);
AggregatorSpec aggregatorSpec = new AggregatorSpec();
if (gatherer != null) {
gatherer.accept(aggregatorSpec);
}
RecipientListRouter recipientListRouter = recipientListRouterSpec.get().getT2();
addComponent(recipientListRouter)
.addComponents(recipientListRouterSpec.getComponentsToRegister());
AggregatingMessageHandler aggregatingMessageHandler = aggregatorSpec.get().getT2();
addComponent(aggregatingMessageHandler);
ScatterGatherHandler messageHandler = new ScatterGatherHandler(recipientListRouter, aggregatingMessageHandler);
return register(new ScatterGatherSpec(messageHandler), scatterGather);
}
/**
* Populate a {@link BarrierMessageHandler} instance for provided timeout.
* @param timeout the timeout in milliseconds.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B barrier(long timeout) {
return barrier(timeout, null);
}
/**
* Populate a {@link BarrierMessageHandler} instance for provided timeout
* and options from {@link BarrierSpec} and endpoint options from {@link GenericEndpointSpec}.
* @param timeout the timeout in milliseconds.
* @param barrierConfigurer the {@link Consumer} to provide {@link BarrierMessageHandler} options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B barrier(long timeout, Consumer<BarrierSpec> barrierConfigurer) {
return register(new BarrierSpec(timeout), barrierConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} instance to perform {@link MessageTriggerAction}.
* @param triggerActionId the {@link MessageTriggerAction} bean id.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B trigger(String triggerActionId) {
return trigger(triggerActionId, null);
}
/**
* Populate a {@link ServiceActivatingHandler} instance to perform {@link MessageTriggerAction}
* and endpoint options from {@link GenericEndpointSpec}.
* @param triggerActionId the {@link MessageTriggerAction} bean id.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B trigger(String triggerActionId,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
MessageProcessor<Void> trigger = new BeanNameMessageProcessor<>(triggerActionId, "trigger");
return handle(new ServiceActivatingHandler(trigger), endpointConfigurer);
}
/**
* Populate a {@link ServiceActivatingHandler} instance to perform {@link MessageTriggerAction}.
* @param triggerAction the {@link MessageTriggerAction}.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B trigger(MessageTriggerAction triggerAction) {
return trigger(triggerAction, null);
}
/**
* Populate a {@link ServiceActivatingHandler} instance to perform {@link MessageTriggerAction}
* and endpoint options from {@link GenericEndpointSpec}.
* @param triggerAction the {@link MessageTriggerAction}.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @return the current {@link IntegrationFlowDefinition}.
*/
public B trigger(MessageTriggerAction triggerAction,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
return handle(new ServiceActivatingHandler(triggerAction, "trigger"), endpointConfigurer);
}
/**
* Represent an Integration Flow as a Reactive Streams {@link Publisher} bean.
* @param <T> the expected {@code payload} type
* @return the Reactive Streams {@link Publisher}
*/
@SuppressWarnings("unchecked")
public <T> Publisher<Message<T>> toReactivePublisher() {
MessageChannel channelForPublisher = this.currentMessageChannel;
Publisher<Message<T>> publisher;
if (channelForPublisher instanceof Publisher) {
publisher = (Publisher<Message<T>>) channelForPublisher;
}
else {
if (channelForPublisher != null) {
publisher = MessageChannelReactiveUtils.toPublisher(channelForPublisher);
}
else {
MessageChannel reactiveChannel = new FluxMessageChannel();
publisher = (Publisher<Message<T>>) reactiveChannel;
channel(reactiveChannel);
}
}
get();
return new PublisherIntegrationFlow<>(this.integrationComponents, publisher);
}
@SuppressWarnings("unchecked")
private <S extends ConsumerEndpointSpec<S, ? extends MessageHandler>> B register(S endpointSpec,
Consumer<S> endpointConfigurer) {
if (endpointConfigurer != null) {
endpointConfigurer.accept(endpointSpec);
}
MessageChannel inputChannel = this.currentMessageChannel;
this.currentMessageChannel = null;
if (inputChannel == null) {
inputChannel = new DirectChannel();
this.registerOutputChannelIfCan(inputChannel);
}
Tuple2<ConsumerEndpointFactoryBean, ? extends MessageHandler> factoryBeanTuple2 = endpointSpec.get();
addComponents(endpointSpec.getComponentsToRegister());
if (inputChannel instanceof MessageChannelReference) {
factoryBeanTuple2.getT1().setInputChannelName(((MessageChannelReference) inputChannel).getName());
}
else {
if (inputChannel instanceof FixedSubscriberChannelPrototype) {
String beanName = ((FixedSubscriberChannelPrototype) inputChannel).getName();
inputChannel = new FixedSubscriberChannel(factoryBeanTuple2.getT2());
if (beanName != null) {
((FixedSubscriberChannel) inputChannel).setBeanName(beanName);
}
registerOutputChannelIfCan(inputChannel);
}
factoryBeanTuple2.getT1().setInputChannel(inputChannel);
}
return addComponent(endpointSpec).currentComponent(factoryBeanTuple2.getT2());
}
private B registerOutputChannelIfCan(MessageChannel outputChannel) {
if (!(outputChannel instanceof FixedSubscriberChannelPrototype)) {
this.integrationComponents.add(outputChannel);
if (this.currentComponent != null) {
String channelName = null;
if (outputChannel instanceof MessageChannelReference) {
channelName = ((MessageChannelReference) outputChannel).getName();
}
Object currentComponent = this.currentComponent;
if (AopUtils.isAopProxy(currentComponent)) {
currentComponent = extractProxyTarget(currentComponent);
}
if (currentComponent instanceof MessageProducer) {
MessageProducer messageProducer =
(MessageProducer) currentComponent;
checkReuse(messageProducer);
if (channelName != null) {
if (messageProducer instanceof AbstractMessageProducingHandler) {
((AbstractMessageProducingHandler) messageProducer).setOutputChannelName(channelName);
}
else {
throw new BeanCreationException("The 'currentComponent' (" + currentComponent
+ ") must extend 'AbstractMessageProducingHandler' "
+ "for message channel resolution by name.\n"
+ "Your handler should extend 'AbstractMessageProducingHandler', "
+ "its subclass 'AbstractReplyProducingMessageHandler', or you should "
+ "reference a 'MessageChannel' bean instead of its name.");
}
}
else {
messageProducer.setOutputChannel(outputChannel);
}
}
else if (currentComponent instanceof SourcePollingChannelAdapterSpec) {
SourcePollingChannelAdapterFactoryBean pollingChannelAdapterFactoryBean =
((SourcePollingChannelAdapterSpec) currentComponent).get().getT1();
if (channelName != null) {
pollingChannelAdapterFactoryBean.setOutputChannelName(channelName);
}
else {
pollingChannelAdapterFactoryBean.setOutputChannel(outputChannel);
}
}
else {
throw new BeanCreationException("The 'currentComponent' (" + currentComponent +
") is a one-way 'MessageHandler' and it isn't appropriate to configure 'outputChannel'. " +
"This is the end of the integration flow.");
}
this.currentComponent = null;
}
}
return _this();
}
private boolean isOutputChannelRequired() {
if (this.currentComponent != null) {
Object currentComponent = this.currentComponent;
if (AopUtils.isAopProxy(currentComponent)) {
currentComponent = extractProxyTarget(currentComponent);
}
return currentComponent instanceof AbstractMessageProducingHandler
|| currentComponent instanceof SourcePollingChannelAdapterSpec;
}
return false;
}
@SuppressWarnings("unchecked")
protected final B _this() {
return (B) this;
}
protected StandardIntegrationFlow get() {
if (this.integrationFlow == null) {
if (this.currentMessageChannel instanceof FixedSubscriberChannelPrototype) {
throw new BeanCreationException("The 'currentMessageChannel' (" + this.currentMessageChannel
+ ") is a prototype for FixedSubscriberChannel which can't be created without MessageHandler "
+ "constructor argument. That means that '.fixedSubscriberChannel()' can't be the last "
+ "EIP-method in the IntegrationFlow definition.");
}
if (this.integrationComponents.size() == 1) {
if (this.currentComponent != null) {
if (this.currentComponent instanceof SourcePollingChannelAdapterSpec) {
throw new BeanCreationException("The 'SourcePollingChannelAdapter' (" + this.currentComponent
+ ") " + "must be configured with at least one 'MessageChannel' or 'MessageHandler'.");
}
}
else if (this.currentMessageChannel != null) {
throw new BeanCreationException("The 'IntegrationFlow' can't consist of only one 'MessageChannel'. "
+ "Add at lest '.bridge()' EIP-method before the end of flow.");
}
}
if (this.implicitChannel) {
Optional<Object> lastComponent = this.integrationComponents.stream().reduce((first, second) -> second);
if (lastComponent.get() instanceof WireTapSpec) {
channel(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
}
}
this.integrationFlow = new StandardIntegrationFlow(this.integrationComponents);
}
return this.integrationFlow;
}
private static boolean isLambda(Object o) {
Class<?> aClass = o.getClass();
return aClass.isSynthetic() && !aClass.isAnonymousClass() && !aClass.isLocalClass();
}
private static Object extractProxyTarget(Object target) {
if (!(target instanceof Advised)) {
return target;
}
Advised advised = (Advised) target;
if (advised.getTargetSource() == null) {
return null;
}
try {
return extractProxyTarget(advised.getTargetSource().getTarget());
}
catch (Exception e) {
throw new BeanCreationException("Could not extract target", e);
}
}
private void checkReuse(MessageProducer replyHandler) {
Assert.isTrue(!REFERENCED_REPLY_PRODUCERS.contains(replyHandler),
"A reply MessageProducer may only be referenced once ("
+ replyHandler
+ ") - use @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) on @Bean definition.");
REFERENCED_REPLY_PRODUCERS.add(replyHandler);
}
}